/**
 * 
 */
package org.vsg.stock.basic.service.handler;

import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.vsg.common.biz.exception.ServiceException;
import org.vsg.stock.basic.domain.VolFigureDetails;
import org.vsg.stock.basic.domain.VolFigureKlineDaily;
import org.vsg.stock.basic.domain.VolFigureKlineDailyPK;
import org.vsg.stock.basic.persistence.VolFigureDetailsDao;
import org.vsg.stock.basic.persistence.VolFigureKlineDailyDao;
import org.vsg.stock.bulk.io.PostgreSQLDataImport;
import org.vsg.stock.core.domain.StockDailyKLine;
import org.vsg.stock.core.indicator.KLineStockIndicatorModel;
import org.vsg.stock.core.indicator.ZigIndicator;
import org.vsg.stock.core.indicator.kline.ZigIndicatorResultItem;

/**
 * @author Bill Vison
 *
 */
public class VolFigureKlineDailyHandlerManager implements ApplicationContextAware {

	/**
	 * set the logger for developer code , do not use System.print(ln) to out print message.
	 */
	private static final Logger logger = LoggerFactory.getLogger(VolFigureKlineDailyHandlerManager.class);
	
	
	
	@Resource
	private VolFigureKlineDailyDao volFigureKlineDailyDao;
	
	@Resource
	private VolFigureDetailsDao volFigureDetailsDao;
	
	static private DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
	
	@Autowired
	private PostgreSQLDataImport postgreSQLDataImport;	

	 private ApplicationContext applicationContext;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}

	public void process(List<StockDailyKLine> stkDailyKLineList , Date startDate , Date endDate ) {
		// --- return out when the size is zero ---
		if ( stkDailyKLineList.size() == 0) {
			return;
		}
		
		
		String stockCode = stkDailyKLineList.get(0).getId().getStockCode();
		
		// --- pre fix handle defined 
		
		KLineStockIndicatorModel klineStkIndiModel = new KLineStockIndicatorModel(stkDailyKLineList ,KLineStockIndicatorModel.KLinePosition.VOL);

		ZigIndicator zigInd = new ZigIndicator();
		zigInd.setThreshold( (double)15 );
		zigInd.setIndicatorModel( klineStkIndiModel );
		

		Map<String, java.io.Serializable > resultMap  = zigInd.calculate();		
		
		// --- use one point value check ---
		List<ZigIndicatorResultItem> crests = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_CREST );
		List<ZigIndicatorResultItem> troughs = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_TROUGH );
		
		// --- invoke and handle processor ----
		Set<VolFigureDetails>  volFigureDetailSet = volFigureDetailsDao.findAll();
		
		List<VolFigureKlineDaily>  currentStockDailyList = null;
		
		
		// ---- manage thread ---
		Vector<Thread> threadVec = new Vector<Thread>();

		long startTime = 0;		
		if ( logger.isInfoEnabled() ) {
			startTime = System.currentTimeMillis();
		}		
		
		// --- invoke processor and defined handler ---
		for (VolFigureDetails detail : volFigureDetailSet) {
			
			// --- load class string from handle ---

			if ( logger.isDebugEnabled() ) {
				startTime = System.currentTimeMillis();
			}

			// --- add config indicator
			if ( detail.getDisabled() != 0 ) {
				continue;
			}					
			
			// --- use thread handle ---
			VolFigureDetailsRunner detalRunner = new VolFigureDetailsRunner(
					stockCode , startDate , endDate , stkDailyKLineList , detail ,applicationContext );
			detalRunner.setCrests(crests);
			detalRunner.setTroughs(troughs);
			
			Thread thread = new Thread(detalRunner);
			
			threadVec.add(thread);
			
			thread.start();
			
			/*
			try {
		
				
				currentStockDailyList = volFigureKlineDailyDao.findVolFiguKlineDailyInCycleByStck(stockCode, detail.getCode(), startDate, endDate);
				
				
				Class className = Class.forName(detail.getProcClassName());
				
				VolFigureKlineDailyProcessor processor = (VolFigureKlineDailyProcessor)BeanUtils.instantiate( className );
				
				processor.setExistedVolFigureKineDaily( currentStockDailyList );
				processor.setCrestsInCycle( crests );
				processor.setTroughsInCycle( troughs );
				processor.setCurrentVolFigureDetails( detail );
				
				List<VolFigureKlineDaily> result = processor.processDailyData( stkDailyKLineList );
				
				// --- insert to write  ---
				if ( result != null && result.size() > 0) {
					persistenceList(result);
				}

			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			*/
			
			
			
			
			if ( logger .isDebugEnabled() ) {
				long usedTimes = System.currentTimeMillis() - startTime;
				
				logger.info("Spend time " + usedTimes + " for [" + detail.getProcClassName() +"] ");
			}
			
		}
		
		for (Thread thread : threadVec) {
			try {
				thread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if ( logger.isInfoEnabled() ) {
			long usedTimes = System.currentTimeMillis() - startTime;
			
			logger.info("Spend time " + usedTimes + " for handle all processor ");
		}
		

		
		// --- pass last object ---
		persistenceLast();
		
		
	}
	
	// --- handle runner
	private class VolFigureDetailsRunner implements Runnable {
		
		private VolFigureDetails detail;
		
		private String stockCode;
		
		private Date startDate;
		
		private Date endDate;
		

		// --- use one point value check ---
		private List<ZigIndicatorResultItem> crests = null;
		
		private List<ZigIndicatorResultItem> troughs = null;
		
		private List<VolFigureKlineDaily>  currentStockDailyList = null;
		
		private List<StockDailyKLine> stkDailyKLineList ;
		
		private ApplicationContext applicationContext;
		
		VolFigureDetailsRunner(String stockCode,
				Date startDate,
				Date endDate ,
				List<StockDailyKLine> stkDailyKLineList , VolFigureDetails detail , ApplicationContext applicationContext) {
			
			this.applicationContext = applicationContext;
			
			this.stockCode = stockCode;
			
			this.startDate = startDate;
			
			this.endDate = endDate;
			
			this.detail = detail;
			
			this.stkDailyKLineList = stkDailyKLineList;
		}
		
		
		public List<ZigIndicatorResultItem> getCrests() {
			return crests;
		}

		public void setCrests(List<ZigIndicatorResultItem> crests) {
			this.crests = crests;
		}

		public List<ZigIndicatorResultItem> getTroughs() {
			return troughs;
		}

		public void setTroughs(List<ZigIndicatorResultItem> troughs) {
			this.troughs = troughs;
		}

		@Override
		public void run() {
			
			
			try {

				currentStockDailyList = volFigureKlineDailyDao.findVolFiguKlineDailyInCycleByStck(stockCode, detail.getCode(), startDate, endDate);
				
				Class className = Class.forName(detail.getProcClassName());
				
				VolFigureKlineDailyProcessor processor = (VolFigureKlineDailyProcessor)BeanUtils.instantiate( className );
				
				processor.setExistedVolFigureKineDaily( currentStockDailyList );
				processor.setCrestsInCycle( crests );
				processor.setTroughsInCycle( troughs );
				processor.setCurrentVolFigureDetails( detail );
				
				
				if ( processor instanceof ApplicationContextAware) {
					ApplicationContextAware aca = (ApplicationContextAware)processor;
					aca.setApplicationContext( this.applicationContext );
				}
				
				List<VolFigureKlineDaily> result = processor.processDailyData( stkDailyKLineList );
				
				// --- insert to write  ---
				if ( result != null && result.size() > 0) {
					persistenceList(result);
				}
				
			} catch (BeanInstantiationException e) {

				e.printStackTrace();
				
			} catch (ClassNotFoundException e) {

				e.printStackTrace();
				
			} finally {
				
			}
			
		}

		
		
	}
	
	
	
	private List<VolFigureKlineDaily> resultProxy = new Vector<VolFigureKlineDaily>();
	
	
	private void persistenceList(List<VolFigureKlineDaily> result) {
		resultProxy.addAll( result );
		if ( resultProxy.size() > 6000) {
			saveToDb(resultProxy);
			resultProxy.clear();
		}
	}
	
	private void persistenceLast() {

		if ( resultProxy.size() > 0) {
			saveToDb(resultProxy);
			resultProxy.clear();
		}
	}	
	
	private void saveToDb(List<VolFigureKlineDaily> result ) {
		
		StringBuffer dataOutput = new StringBuffer();
		VolFigureKlineDailyPK pk = null;
		for (VolFigureKlineDaily daily : result) {
			pk = daily.getId();
			dataOutput.append(pk.getStockCode()).append(",");
			dataOutput.append(pk.getCodeVolFigureDetails()).append(",");
			dataOutput.append(df.format( pk.getStockStartDate() )).append(",");
			if ( daily.getStockDateEnd() != null) {
				dataOutput.append(df.format( daily.getStockDateEnd() ));
			} 
			dataOutput.append("\n");
			
		}
		
		
		// --- import new data ----
		InputStream is  = null;
		
		postgreSQLDataImport.connect();
		
		String[] fields = {
				"stock_code" ,
				"code_vol_figure_details",
				"stock_date_start",
				"stock_date_end"};
		
		try {

			is = IOUtils.toInputStream(dataOutput.toString());
			
			// --- import data ----
			postgreSQLDataImport.importData("vol_figure_kline_daily", is, fields);
			
			if ( logger.isDebugEnabled() ) {
				String stkCode = result.get(0).getId().getStockCode();
				logger.debug("Stock ["+stkCode+"] vol figure imported!");
			}
			
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		postgreSQLDataImport.disconnect();		
	}
	

}
